<?php
/**************************************************************************/
/* SmartDB: smart & fast SQL constructor
/* ver. 0.1 dev
/* ===========================
/*
/* Copyright (c)oded 2007 by WP
/* http://smartdb.keeperweb.com
/* 														
/* mysql5.driver.php: MySQL 5 driver
/**************************************************************************/
if (defined('SMARTDB_MYSQL_LOADED')) {return self::$db = new SmartDB_mysql;}
else
{
if (!function_exists('hecho'))
{
 function hecho($string) {return preg_replace('~.~se','sprintf("\\x%02x",ord("$0"))',$string);}
}
if (!function_exists('ftok'))
{
 function ftok($path,$proj)
 {
  $st = stat($path);
  if (!$st) {return -1;}
  return (($st['ino'] & 0xffff) | (($st['dev'] & 0xff) << 16) | (($proj & 0xff) << 24));
 }
}
if (!function_exists('flock_wait'))
{
 function flock_wait($fp,$lock)
 {
  if ($fp === FALSE) {return FALSE;}
  while (TRUE)
  {
   if (flock($fp,$lock)) {return TRUE;}
   usleep(100000);
  }
 }
}
define('FIELDTYPE_INT',1);
define('FIELDTYPE_CHAR',2);
define('FIELDTYPE_NULL',3);
define('FIELDTYPE_AS_IS',4);
define('FIELDTYPE_FLOAT',5);
class SmartDB_mysql extends SmartDB_prototype
{
 public $qcache_path;
 public $resultcache_path;
 public $hints = TRUE;
 public $pointer;
 public $server = 'localhost';
 public $username = 'root';
 public $password = '';
 public $database = '';
 public $current_database = '';
 public $persistent = FALSE;
 public $flags = 0;
 public $errorHandler;
 public $trace = FALSE;
 public $trace_stack = array();
 public $encoding = NULL;
 public $auto_reconnect = TRUE;
 public $memcache;
 public $memcache_prefix = '';
 public function is_cached_result($id,$lifetime = -1,$storage = 'file')
 {
  if ($storage == 'memcache')
  {
   return TRUE;
  }
  $p = $this->resultcache_path.$id;
  return file_exists($p.'.h') and ($lifetime == -1 or (filemtime($p.'.h') > time()-$lifetime));
 }
 public function memcache_load()
 {
  require_once SMARTDB_DIR.'MemcacheFacade.class.php';
  $this->memcache = new SmartDB_MemcacheFacade;
  $this->memcache->prefix = $this->memcache_prefix;
 }
 public function clearResultCache($id = '*')
 {
  if (strpos($id,'*') === FALSE)
  {
   if (file_exists(($p = $this->resultcache_path.$id.'.h'))) {unlink($p);}
   if (file_exists(($p = $this->resultcache_path.$id.'.data'))) {unlink($p);}
   if (file_exists(($p = $this->resultcache_path.$id.'.index'))) {unlink($p);}
  }
  else
  {
   $h = opendir($this->resultcache_path);
   if (!$h) {return FALSE;}
   while (($f = readdir($h)) !== FALSE)
   {
    if (is_file($this->resultcache_path.$f) and fnmatch($id,$f)) {unlink($this->resultcache_path.$f);}
   }
  }
  return TRUE;
 }
 public function errHandler($err,$query)
 {
  echo 'SmartDB query error #'.$err['code'].': '.$err['message'].' in query \''.$query.'\'.';
 }
 public function error()
 {
  return array(
   'code' => $this->pointer?mysql_errno($this->pointer):-1,
   'message' => $this->pointer?mysql_error($this->pointer):NULL
  );
 }
 public function handleError()
 {
  if ($this->errorHandler) {$h = $this->errorHandler;}
  else {$h = array($this,'errHandler');}
  call_user_func($h,$this->error(),$this->last_query);
 }
 public function numrows($mixed = NULL)
 {
  if ($mixed === NULL)
  {
   if ($this->lastQuery->result === NULL) {$this->lastQuery->execute();}
   return $this->lastQuery->numrows();
  }
  if (is_resource($mixed))
  {
   return mysql_num_rows($mixed);
  }
  elseif (is_object($mixed))
  {
   return $mixed->numrows();
  }
  else
  {
   return FALSE;
  }
 }
 public function affectedrows() {return mysql_affected_rows($this->pointer);}
 public function nextid() {return mysql_insert_id($this->pointer);}
 public function insertId() {return mysql_insert_id($this->pointer);}
 public function insertId_real()
 {
  $row = $this->query_fetchrow('SELECT LAST_INSERT_ID() AS `id`');
  return $row['id'];
 }
 public function total_found_rows()
 {
  $row = $this->query_fetchrow('SELECT FOUND_ROWS() AS `num`');
  return $row['num'];
 }
 public function truncate($tables)
 {
  $q = new SmartDB_mysql_query($this);
  $q->query = 'TRUNCATE TABLE '.$q->prepare_mixed($tables,2);
  return $q->execute();
 }
 public function connect()
 {
  if ($this->persistent) {$this->pointer = mysql_pconnect($this->server,$this->username,$this->password,$this->flags);}
  else {$this->pointer = mysql_connect($this->server,$this->username,$this->password,NULL,$this->flags);}
  if (!$this->pointer) {$this->handleError(); return FALSE;}
  $this->select_db();
  if ($this->encoding !== NULL)
  {
   $q = new SmartDB_mysql_query($this);
   $q->query = 'SET NAMES '.$this->encoding;
   $q->execute();
  }
  return TRUE;
 }
 public function close()
 {
  return mysql_close($this->pointer);
 }
 public function numfields($result = NULL) {return mysql_num_fields($result === NULL?$this->lastQuery->result:$result);}
 public function fieldname($offset,$result = NULL) {return mysql_field_name($result === NULL?$this->lastQuery->result:$result,$offset);}
 public function fieldtype($offset,$result = NULL) {return mysql_field_name($result === NULL?$this->lastQuery->result:$result,$offset);}
 public function rowseek($rownum,$result = NULL) {return mysql_data_seek($result === NULL?$this->lastQuery->result:$result,$offset);}
 public function freeresult($mixed = NULL)
 {
  if ($mixed === NULL)
  {
   if (($this->lastQuery->result === NULL) && !$this->lastQuery->is_cached) {$this->lastQuery->execute();}
   return $this->lastQuery->freeresult();
  }
  if (is_resource($mixed))
  {
   return mysql_free_result($mixed);
  }
  elseif (is_object($mixed))
  {
   return $mixed->freeresult();
  }
  else
  {
   return FALSE;
  }
 }
 public function begin() {return $this->query('BEGIN');}
 public function rollback() {return $this->query('ROLLBACK');}
 public function commit() {return $this->query('COMMIT');}
 public function query_fetchrow($query,$cache = FALSE,$cache_id = NULL,$cache_lifetime = -1)
 {
  $q = $this->query($query,$cache,$cache_id,$cache_lifetime);
  $row = $q->fetchRow();
  $q->freeresult();
  return $row;
 }
 public function query_fetchrow_object($query,$cache = FALSE,$cache_id = NULL,$cache_lifetime = -1)
 {
  $q = $this->query($query,$cache,$cache_id,$cache_lifetime);
  $row = (object) $q->fetchRow();
  $q->freeresult();
  return $row;
 }
 public function query_fetchrowset($query,$cache = FALSE,$cache_id = NULL,$cache_lifetime = -1)
 {
  $q = $this->query($query,$cache,$cache_id,$cache_lifetime);
  $rows = $q->fetchRowSet();
  $q->freeresult();
  return $rows;
 }
 public function fetchrowset($mixed = NULL)
 {
  if ($mixed === NULL)
  {
   return $this->lastQuery->fetchRowset();
  }
  if (is_resource($mixed))
  {
   $rows = array();
   while ($row = mysql_fetch_assoc($mixed))
   {
    $rows[] = $rows;
   }
   return $rows;  
  }
  elseif (is_object($mixed))
  {
   return $mixed->fetchRowset();
  }
  else
  {
   return FALSE;
  }
 }
 public function fetchrow($mixed = NULL)
 {
  if ($mixed === NULL)
  {
   if ($this->lastQuery->result === NULL) {$this->lastQuery->execute();}
   return $this->lastQuery->fetchRow();
  }
  if (is_resource($mixed))
  {
   return mysql_fetch_assoc($mixed);
  }
  elseif (is_object($mixed))
  {
   return $mixed->fetchRow();
  }
  else
  {
   return FALSE;
  }
 }
 public function select_db($dbname = NULL)
 {
  if ($dbname === NULL) {$dbname = $this->database;}
  else {$this->database = $dbname;}
  if ($this->current_database == $dbname) {return TRUE;}
  if ($this->pointer !== NULL)
  {
   if (!mysql_select_db($dbname,$this->pointer)) {$this->handleError(); return FALSE;}
   $this->current_database = $dbname;
  }
  return TRUE;
 }
 public function process_val($input)
 {
  $fieldtype = FIELDTYPE_CHAR;
  $maxlen = -1;
  $val = NULL;
  if (is_array($input))
  {
   if (isset($input[0])) {$val = $input[0];}
   if (isset($input[1])) {$fieldtype = $input[1];}
   if (isset($input[2])) {$maxlen = $input[2];}
  }
  else
  {
   $val = $input;
   if (is_int($val)) {$fieldtype = FIELDTYPE_INT;}
   elseif (is_float($val)) {$fieldtype = FIELDTYPE_FLOAT;}
   else {$fieldtype = FIELDTYPE_CHAR;}
  }
  if ($fieldtype == FIELDTYPE_AS_IS)
  {
   if ($val === NULL) {$val = 'NULL';}
   return $val;
  }
  if ($fieldtype == FIELDTYPE_INT)	 {return getint($val);}
  if ($fieldtype == FIELDTYPE_FLOAT) {return '\''.floatval($val).'\'';}
  if ($fieldtype == FIELDTYPE_CHAR)
  {
   $val = strval($val);
   if ($maxlen >= 0) {$val = substr($val,0,$maxlen);}
   return '\''.$this->escape($val).'\'';
  }
  if ($fieldtype == FIELDTYPE_NULL)  {return 'NULL';}
 }
 public function escape($string)
 {
  static $sqlescape = array(
	"\x00"=> '\0',
	"\n"	=> '\n',
	"\r"	=> '\r',
	'\\'	=> '\\\\',
	'\''	=> '\\\'',
	'"'		=> '\\"'
  );
  return strtr($string,$sqlescape);
 }
 function like_escape($string)
 {
  static $sqlescape = array(
	"\x00"	=> '\0',
	"\n"	=> '\n',
	"\r"	=> '\r',
	'\\'	=> '\\\\',
	'\''	=> '\\\'',
	'"'		=> '\\"',
	'%'		=> '\%',
	'_'		=> '\_'
  );
  return strtr($string,$sqlescape);
 }
 public function query($query,$cache = FALSE,$cache_id = NULL,$cache_lifetime = NULL)
 {
  $q = new SmartDB_mysql_query($this);
  $q->query = $query;
  if (substr($query,0,1) == 'S' or substr($query,0,1) == 's')
  {
   $q->qtype = 'SELECT';
   if ($cache) {return $q->cachedExecute($cache_id,$cache_lifetime);}
  }
  else {return $q->execute();}
  return $q;
 }
 public function select($fields)
 {
  return new SmartDB_mysql_select($fields,$this);
 }
 public function insert($table,$row = NULL,$multi = NULL,$type = NULL)
 {
  $q = new SmartDB_mysql_insert($table,'INSERT',$this,$row,$multi,$type);
  if ($row !== NULL) {return $this->insertId();}
  return $q;
 }
 public function replace($table,$row = NULL,$multi = NULL,$type = NULL)
 {
  $q = new SmartDB_mysql_insert($table,'REPLACE',$this,$row,$multi,$type);
  if ($row !== NULL) {return $this->insertId();}
  return $q;
 }
 public function update($table,$row = NULL,$where = NULL,$limit = NULL,$type = NULL,$postfix = NULL)
 {
  if ($limit === '') {$limit = NULL;}
  if (!sizeof($row)) {return 0;}
  $q = new SmartDB_mysql_update($table,$this,$row,$where,$limit,$type,$postfix);
  if ($row !== NULL) {return $this->affectedrows();}
  return $q;
 }
 public function delete($table,$fields = NULL,$where = NULL,$limit = NULL,$type = NULL,$postfix = NULL)
 {
  $q = new SmartDB_mysql_delete($table,$this,$fields,$where,$limit,$type,$postfix);
  if ($row !== NULL) {return $this->affectedrows();}
  return $q;
 }
 public function buffered_update($table,$row = NULL,$where = NULL,$limit = NULL,$type = NULL,$postfix = NULL)
 {
  return new SmartDB_mysql_update($table,$this,$row,$where,$limit,$type,$postfix,TRUE);
 }
}
class SmartDB_mysql_result
{
 public $result;
 public $driver;
 public $num;
 public $rows;
 public $offset = 0;
 public $limit = NULL;
 public $_datafp;
 public $_indexfp;
 public $_headerfp;
 public $pos = 0;
 public $fields = NULL;
 public $_shm_id_h;
 public $_shm_id_i;
 public $_shm_id_d;
 public $order;
 public $is_cached = FALSE;
 public $updateCache = FALSE;
 public function __construct($query)
 {
  $this->result = &$query->result;
  $this->query = $query;
  $this->driver = $query->driver;
  if ($this->cache = $query->cache)
  {
   $this->is_cached = $this->driver->is_cached_result($this->query->cache_id,$this->query->cache_lifetime,$this->query->cacheStorage);
  }
  else {$this->is_cached = FALSE;}
 }
 public function limit($a,$b = NULL)
 {
  if ($b !== NULL)
  {
   $this->offset = (int) $a;
   $this->limit = (int) $b;
  }
  else {$this->limit = (int) $a;}
  return $this;
 }
 public function paginate($pagenum,$perpage,$max = NULL)
 {
  $this->offset = $pagenum*$perpage;
  $this->limit = (int) $perpage;
  if ($max !== NULL)
  {
   $s = $this->offset + $this->limit ;
   if ($s > $max)
   {
    if ($s - $max <= $this->limit) {$this->limit -= $s - $max;}
    else {$this->offset -= $s-$max;}
   }
  }
  return $this;
 }
 public function order($expr,$desc = FALSE)
 {
  $this->order[] = array($expr,$desc);
  return $this;
 }
 public function resetOrder() {$this->order = array();}
 public function num() {if ($this->num !== NULL) {return $this->num;} $this->num = mysql_num_rows($this->result);}
 public function parse_index($fp)
 {
  rewind($fp);
  $t = unpack('n',fread($fp,2));
  $indexlen = $t[1];
  $index = array();
  $seq = 8;
  while (($r = fread($fp,$indexlen)))
  {
   $t = unpack('Noffset/Nrlen',substr($r,0,$seq));
   $t['fields'] = array();
   for ($i = 0; $i < $t['numfields']; ++$i)
   {
    $t2 = unpack('Noffset/Nlen',substr($r,$seq+8*$i,8));
    $t['fields'][] = array($t2['offset'],$t2['len']);
   }
   ++$i;
   $index[] = $t;
  }
  return $index;
 }
 public function fields($arr = NULL)
 {
  if (is_array($arr)) {$this->fields = $arr;}
  else {$this->fields = func_get_args();}
  return $this;
 }
 public function onCacheInit($callback)
 {
  $this->onCacheInit = $callback;
  return $this;
 }
 public function cacheFilter($callback)
 {
  $this->cacheFilter = $callback;
  return $this;
 }
 public function updateCache($bool = TRUE)
 {
  $this->updateCache = $bool;
  return $this;
 }
 public function cachedFetchrowset()
 {
  $p = $this->driver->resultcache_path.$this->query->cache_id;
  $rows = array();
  if ($this->query->cacheStorage == 'memory_if_possible')
  {
   $this->query->cacheStorage = function_exists('shmop_open')?'memory':'file';
  }
  if ($this->is_cached)
  {
   if ($this->query->cacheStorage == 'memcache')
   {
    if (!isset($this->driver->memcache)) {$this->driver->memcache_load();}
    if ((!$this->updateCache) && (($r = $this->driver->memcache->get($this->query->cache_id)) !== FALSE))
    {
     return $r;
    }
    $this->updateCache = FALSE;
    if ($this->result === NULL)
    {
     if (!$this->query->result = $this->query->sendQuery()) {$this->driver->handleError(); return FALSE;}
    }
    $rows = isset($this->onCacheInit)?call_user_func($this->onCacheInit,$this):array();
    while($row = mysql_fetch_assoc($this->query->result)) {$rows[] = $row;}
    $this->query->freeresult();
    if (isset($this->cacheFilter)) {$rows = call_user_func($this->cacheFilter,$this,$rows);}
    $this->driver->memcache->set($this->query->cache_id,$rows,$this->query->cache_lifetime);
    return $this->rows = $rows;
   }
   if ($this->query->cacheStorage == 'memory')
   {
    $use_mem = TRUE;
    if (!$this->_shm_id_h)
    {
     $shm_key_h = ftok($p.'.h','h');
     if (!$this->_shm_id_h = shmop_open($shm_key_h,'a',0,0))
     {
      $use_mem = FALSE;
     }
    }
    if (!$this->_shm_id_i)
    {
     $shm_key_i = ftok($p.'.index','i');
     if (!$this->_shm_id_i = shmop_open($shm_key_i,'a',0,0))
     {
      $use_mem = FALSE;
     }
    }
    if (!$this->_shm_id_d)
    {
     $shm_key_d = ftok($p.'.data','d');
     if (!$this->_shm_id_d = shmop_open($shm_key_d,'a',0,0))
     {
      $use_mem = FALSE;
     }
    }
   }
   else {$use_mem = FALSE;}
   if ($use_mem)
   {
    $i_pos = $h_pos = $d_pos = 0;
    $t = unpack('nl/nn',shmop_read($this->_shm_id_i,$i_pos,4)); $i_pos += 4;
    $this->_indexlen = $t['l'];
    $this->num = $t['n'];
    $t = unpack('n',shmop_read($this->_shm_id_h,$h_pos,2)); $h_pos += 2;
    $this->_numfields = $t[1];
    if (!$this->_numfields) {return $this->rows = array();}
    $this->_fields = array();
    $index = array();
    for ($i = 0; $i < $this->_numfields; ++$i)
    {
     $t = unpack('n',shmop_read($this->_shm_id_h,$h_pos,2)); $h_pos += 2;
     $this->_fields[] = shmop_read($this->_shm_id_h,$h_pos,$t[1]); $h_pos += $t[1];
    }
    $j = 0;
    if ($this->offset > 0) {$i_pos += $this->offset*$this->_indexlen;}
    $seq = 8;
    if ($this->fields !== NULL)
    {
     $d = array();
     for ($i = 0,$n = sizeof($this->_fields); $i < $n; ++$i)
     {
      if (in_array($this->_fields[$i],$this->fields)) {$d[$i] = TRUE;}
     }
    }
    else {$d = FALSE;}
    while (($r = @shmop_read($this->_shm_id_i,$i_pos,$this->_indexlen)))
    {
     $i_pos += $this->_indexlen;
     if ($this->limit !== NULL and $j >= $this->limit) {break;}
     $t = unpack('No/Nl',substr($r,0,$seq));
     $row = array();
     if ($j == 0) {$d_pos = $t['o'];}
     $n = $this->_numfields;
     for ($i = 0; $i < $n; ++$i)
     {
      if ($d)
      {
       if (!isset($d[$i])) {continue;}
       $t2 = unpack('No/Nl',substr($r,$seq+8*$i,8));
       $d_pos = $t2['o'];
      }
      else {$t2 = unpack('Nl',substr($r,$seq+8*$i+4,4));}
      $row[$this->_fields[$i]] = ($t2['l'] == 0)?'':shmop_read($this->_shm_id_d,$d_pos,$t2['l']);
      $d_pos += $t2['l'];
     }
     $rows[] = $row;
     //$index[] = $t;
     ++$j;
    }
    return $rows;
   }
   $this->_datafp = fopen($p.'.data','r');
   flock_wait($this->_datafp,LOCK_SH);
   if (!$this->_datafp) {return FALSE;}
   $this->_hfp = fopen($p.'.h','r');
   if (!$this->_hfp) {return FALSE;}
   flock_wait($this->_hfp,LOCK_SH);
   $this->_indexfp = fopen($p.'.index','r');
   if (!$this->_indexfp) {return FALSE;}
   flock_wait($this->_indexfp,LOCK_SH);
   if ($this->_restore_memorycache($p)) {return $this->cachedFetchrowset();}

   $t = unpack('n',fread($this->_indexfp,2));
   $this->_indexlen = $t[1];
   $t = unpack('n',fread($this->_indexfp,2));
   $this->num = $t[1];
   $t = unpack('n',fread($this->_hfp,2));
   $this->_numfields = $t[1];
   if (!$this->_numfields) {return $this->rows = array();}
   $this->_fields = array();
   $index = array();
   for ($i = 0; $i < $this->_numfields; ++$i)
   {
    $len = unpack('n',fread($this->_hfp,2));
    $this->_fields[] = fread($this->_hfp,$len[1]);
   }
   $order = (isset($this->order[0][1]) && ($this->order[0][1] == 'DESC'))?FALSE:TRUE;
   if ($order) {$spos = $cpos = ftell($this->_indexfp);}
   else {$spos = $cpos = ftell($this->_indexfp)+(($this->num > 1?$this->num:1)-1)*$this->_indexlen;}
   $j = 0;
   if ($this->offset > 0)
   {
    if ($order) {fseek($this->_indexfp,$cpos = $spos+$this->offset*$this->_indexlen);}
    else {fseek($this->_indexfp,$cpos = $spos-$this->offset*$this->_indexlen);}
   }
   $seq = 8;
   if ($this->fields !== NULL)
   {
    $d = array();
    for ($i = 0,$n = sizeof($this->_fields); $i < $n; ++$i)
    {
     if (in_array($this->_fields[$i],$this->fields)) {$d[$i] = TRUE;}
    }
   }
   else {$d = FALSE;}
   while (($r = fread($this->_indexfp,$this->_indexlen)))
   {
    if (($this->limit !== NULL) && ($j >= $this->limit)) {break;}
    if ($j >= $this->num) {break;}
    
    if ($order === FALSE)
    {
     if ($j > 0)
     {
      $cpos -= $this->_indexlen;
      fseek($this->_indexfp,$cpos);
      //fseek($this->_indexfp,$spos-($this->offset+$j)*$this->_indexlen);
     }
    //var_dump($cpos,$spos-($this->offset+$j)*$this->_indexlen);
    }
    
    $t = unpack('No/Nl',substr($r,0,$seq));
    $row = array();
    if ($j == 0) {fseek($this->_datafp,$t['o']);}
    $n = $this->_numfields;
    for ($i = 0; $i < $n; ++$i)
    {
     if ($d)
     {
      if (!isset($d[$i])) {continue;}
      $t2 = unpack('No/Nl',substr($r,$seq+8*$i,8));
      fseek($this->_datafp,$t2['o']);
     }
     else {$t2 = unpack('Nl',substr($r,$seq+8*$i+4,4));}
     $row[$this->_fields[$i]] = ($t2['l'] == 0)?'':fread($this->_datafp,$t2['l']);
    }
    $rows[] = $row;
    //$index[] = $t;
    ++$j;
   }
  }
  else
  {
   if ($this->result === NULL)
   {
    if (!$this->query->result = $this->query->sendQuery()) {$this->driver->handleError(); return FALSE;}
   }
   $rows = array();
   $offset = 0;
   $index = array();
   $this->_fields = NULL;
   $this->_datafp = fopen($p.'.data','w+');
   if (!$this->_datafp) {return FALSE;}
   flock_wait($this->_datafp,LOCK_EX);
   $this->_hfp = fopen($p.'.h','w+');
   if (!$this->_hfp) {return FALSE;}
   flock_wait($this->_hfp,LOCK_EX);
   $tmp_idx = tmpfile();
   $this->_indexfp = fopen($p.'.index','w+');
   if (!$this->_indexfp) {return FALSE;}
   flock_wait($this->_indexfp,LOCK_EX);
   $j = $c = 0;
   $this->num();
   $d = FALSE;
   while (TRUE)
   {
    if (isset($this->rows[$j])) {$row = $this->rows[$j];}
    else
    {
     if ($j >= $this->num) {break;}
     mysql_data_seek($this->result,$j);
     $row = mysql_fetch_assoc($this->result);
    }
    if (($j >= $this->offset) && (($this->limit === NULL) || ($c < $this->limit)))
    {
     if ($this->fields !== NULL)
     {
      if (!$d)
      {
       $d = array();
       for ($i = 0,$n = sizeof($this->_fields); $i < $n; ++$i)
       {
        if (!in_array($this->_fields[$i],$this->fields)) {$d[$i] = TRUE;}
       }
      }
      $n = sizeof($this->_fields);
      for ($i = 0; $i < $n; ++$i)
      {
       if (isset($d[$i])) {unset($row[$this->_fields[$i]]);}
      }
     }
     $rows[] = $row; ++$c;
    }
    $rlen = 0;
    $h = pack('NN',$offset,$rlen);
    $rfields = array();
    if (!$this->_fields) {$this->_fields = array_keys($row);}
    for ($i = 0,$s = sizeof($row); $i < $s; ++$i)
    {
     fwrite($this->_datafp,$row[$this->_fields[$i]]);
     $h .= pack('NN',$offset+$rlen,strlen($row[$this->_fields[$i]]));
     $rlen += strlen($row[$this->_fields[$i]]);
    }
    fwrite($tmp_idx,$h);
    if ($j == 0)
    {
     $this->_indexlen = strlen($h);
    }
    
    $offset += $rlen;
    ++$j;
   }
   $this->query->freeresult();
   if ($j == 0)
   {
    fwrite($this->_indexfp,pack('n',0));
    fwrite($this->_indexfp,pack('n',0));
    fwrite($this->_hfp,pack('n',0));
   }
   else
   {
    fwrite($this->_indexfp,pack('n',$this->_indexlen));
    $n = sizeof($this->_fields);
    fwrite($this->_hfp,pack('n',$n));
    for ($i = 0; $i < $n; ++$i) {fwrite($this->_hfp,pack('n',strlen($this->_fields[$i])).$this->_fields[$i]);}
    fwrite($this->_indexfp,pack('n',$j));
    rewind($tmp_idx);
    stream_copy_to_stream($tmp_idx,$this->_indexfp);
    fclose($tmp_idx);
   }
   $this->_restore_memorycache($p);
   fclose($this->_indexfp);
   fclose($this->_datafp);
   fclose($this->_hfp);
  }
  return $this->rows = $rows;
 }
 public function _restore_memorycache($p)
 {
  if ($this->query->cacheStorage == 'memory')
  {
   $shm_key_h = ftok($p.'.h','h');
   if ($this->_shm_id_h = shmop_open($shm_key_h,'a',0,0)) {shmop_delete($this->_shm_id_h);}
   $this->_shm_id_h = shmop_open($shm_key_h,'c',0755,ftell($this->_hfp));
   rewind($this->_hfp);
   while (!feof($this->_hfp))
   {
    $pos = ftell($this->_hfp);
    shmop_write($this->_shm_id_h,fread($this->_hfp,1024*10),$pos);
   }
   shmop_close($this->_shm_id_h);
   $shm_key_i = ftok($p.'.index','i');
   if ($this->_shm_id_i = shmop_open($shm_key_i,'a',0,0)) {shmop_delete($this->_shm_id_i);}
   $this->_shm_id_i = shmop_open($shm_key_i,'c',0755,ftell($this->_indexfp));
   rewind($this->_indexfp);
   while (!feof($this->_indexfp))
   {
    $pos = ftell($this->_indexfp);
    shmop_write($this->_shm_id_i,fread($this->_indexfp,1024*10),$pos);
   }
   shmop_close($this->_shm_id_i);
   $shm_key_d = ftok($p.'.data','d');
   if ($this->_shm_id_d = shmop_open($shm_key_d,'a',0,0)) {shmop_delete($this->_shm_id_d);}
   $this->_shm_id_d = shmop_open($shm_key_d,'c',0755,ftell($this->_datafp));
   rewind($this->_datafp);
   while (!feof($this->_datafp))
   {
    $pos = ftell($this->_datafp);
    shmop_write($this->_shm_id_d,fread($this->_datafp,1024*10),$pos);
   }
   shmop_close($this->_shm_id_d);
   return TRUE;
  }
  return FALSE;
 }
 public function fetchRowset()
 {
  if ($this->rows !== NULL) {return $this->rows;}
  if ($this->cache) {return $this->cachedFetchrowset();}
  $this->num();
  $rows = array();
  while($row = mysql_fetch_assoc($this->result)) {$rows[] = $row;}
  $this->query->freeresult();
  return $this->rows = $rows;
 }
 public function nonCachedFetchRowset()
 {
  if ($this->rows !== NULL) {return $this->rows;}
  if ($this->result === NULL) {$this->query->execute();}
  $rows = array();
  $this->num();
  while ($row = mysql_fetch_assoc($this->result)) {$rows[] = $row;}
  //$this->query->freeresult();
  return $this->rows = $rows;
 }
 public function fetchRow()
 {
  if ($this->cache)
  {
   if ($this->query->cacheStorage == 'memcache')
   {
    if ($this->rows === NULL) {$this->rows = $this->cachedFetchrowset();}
   }
   else
   {
    $this->offset = $this->pos++;
    $this->limit = 1;
    $r = $this->cachedFetchrowset();
    return $r[0];
   }
  }
  if ($this->rows !== NULL) {$p = $this->pos; ++$this->pos; return isset($this->rows[$p])?$this->rows[$p]:FALSE;}
  ++$this->pos;
  return mysql_fetch_assoc($this->result);
 }
 public function freeresult()
 {
  return mysql_free_result($this->result);
 }
}
class SmartDB_mysql_query
{
 public $qtype = 'void';
 public $qcache = NULL;
 public $cache = FALSE;
 public $result;
 public $unbuffered = FALSE;
 public $cachePartVal;
 public $cachePartType;
 public $cacheStorage = 'file';
 public $is_cached = FALSE;
 public function unbuffered($v = TRUE) {$this->unbuffered = $v; return $this;}
 public function sendQuery()
 {
  if ($this->driver->pointer === NULL) {if (!$this->driver->connect()) {return FALSE;}}
  if ($this->unbuffered) {$result =  mysql_unbuffered_query($this->query,$this->driver->pointer);}
  else {$result = mysql_query($this->query,$this->driver->pointer);}
  return $result;
 }
 public function numrows()
 {
  if ($this->result === NULL) {$this->execute();}
  return mysql_num_rows($this->result);
 }
 public function fetchRow()
 {
  if ($this->result === NULL) {$this->execute();}
  return $this->result?mysql_fetch_assoc($this->result):FALSE;
 }
 public function fetchRowset()
 {
  if ($this->result === NULL) {$this->execute();}
  $rows = array();
  while ($row = mysql_fetch_assoc($this->result))
  {
   $rows[] = $row;
  }
  return $rows;
 }
 public function __construct($driver)
 {
  $this->driver = $driver;
  $this->driver->lastQuery = $this;
 }
 public function freeresult() {if (!$this->result) {return FALSE;} mysql_free_result($this->result); $this->result = FALSE;}
 public function query($query,$cache = FALSE,$cache_id = NULL,$cache_lifetime = -1)
 {
  if ($this->driver->trace) {$this->driver->trace_stack[] = array($query,$cache);}
  $this->driver->last_query = $query;
  $this->query = $query;
  if (!$cache)
  {
   if (!$this->result = $this->sendQuery($query))
   {
    if ((mysql_errno($this->driver->pointer) == 2006) and $this->driver->auto_reconnect)
    {
     $this->driver->connect();
     return $this->query($this->query);
    }
    $this->driver->handleError();
    return FALSE;
   }
  }
  else
  {
   if ($cache_id === NULL) {$cache_id = sprintf('%x',crc32($query));}
   $this->cache = TRUE;
   $this->cache_id = $cache_id;
   $this->cache_lifetime = $cache_lifetime;
   return $this->qresult = new SmartDB_mysql_result($this);
  }
  if ($this->qtype == 'SELECT') {return $this->qresult = new SmartDB_mysql_result($this);}
  else {return $this;}
 }
 public function qcache($id)
 {
  $this->qcache = $id;
  return $this;
 }
 public function get_query()
 {
  return $this->query;
 }
 public function cachePartitioning($val,$type = 0)
 {
  $this->cachePartVal = $val;
  $this->cachePartType = $type;
  return $this;
 }
 public function cacheStorage($val = 'file')
 {
  $this->cacheStorage = $val;
  return $this;
 }
 public function cachedExecute($id = NULL,$lifetime = -1)
 {
  return $this->query($q = $this->get_query(),TRUE,$id,$lifetime);
 }
 public function execute()
 {
  $query = $this->get_query();
  if ($query !== NULL) {return $this->query($query);}
  return;
 }
 public function prepare_mixed($mixed,$mode = 1)
 {
  if (is_array($mixed))
  {
   if ($mode == 4)
   {
    $r = '';
    for ($i = 0,$s = sizeof($mixed); $i < $s; ++$i)
    {
     $r .= ($i > 0?', ':'').$mixed[$i][0].($mixed[$i][1]?' DESC':'');
    }
    return $r;
   }
   elseif ($mode == 1 || $mode == 2)
   {
    $r = '';
    $s = sizeof($mixed);
    $i = 0;
    foreach ($mixed as $k => $v)
    {
     if (!ctype_digit((string) $k)) {$as = $v; $v = $k;}
     else {$as = '';}
     if ((strpos($v,'.') !== FALSE) && $this->driver->hints)
     {
      if (strpos($v,'`') === FALSE)
      {
       $e = explode('.',$v);
       $r .= '`'.$e[0].'`';
       if (isset($e[1])) {$r .= '.`'.$e[1].'`';}
       if (isset($e[2])) {$r .= '.`'.$e[2].'`';}
      }
      else {$r .= $v;}
     }
     else
     {
      if ($this->driver->hints)
      {
       if (strpos($v,'`') === FALSE) {$v = '`'.$v.'`';}
      }
      $r .= $v;
     }
     if ($as !== '') {$r .= ' AS `'.$r.'`';}
     if ($i+1 < $s) {$r .= ', ';}
     ++$i;
    }
    return $r;
   }
   elseif ($mode == 3)
   {
    $r = '';
    $i = 0;
    $s = sizeof($mixed);
    foreach ($mixed as $k=>$v)
    {
     if (is_int($k) && is_array($v)) {$k = $v[0]; $v = $v[1];}
     if (is_int($k))
     {
      if (is_array($v)) {$k = $v[0]; $v = $v[1];}
      else {$r .= ' '.$v.' ';  continue;}
     }
     $operator = '=';
     $e = explode(' ',$k);
     if (isset($e[1])) {$operator = implode(' ',array_slice($e,1));}
     $k = $e[0];
     if (strpos($k,'.') !== FALSE)
     {
      $e = explode('.',$k);
      $r .= '`'.$e[0].'`';
      if (isset($e[1])) {$r .= '.`'.$e[1].'`';}
      if (isset($e[2])) {$r .= '.`'.$e[2].'`';}
     }
     else
     {
      if ($this->driver->hints)
      {
       if (strpos($k,'`') === FALSE) {$k = '`'.$k.'`';}
      }
      $r .= $k;
     }
     $r .= ' '.$operator.' '.$this->driver->process_val($v);
    }
    ++$i;
    return $r;
   }
  }
  elseif ($this->driver->hints)
  {
   if ($mode == 2)
   {
    if (strpos($mixed,'`') === FALSE and strpos($mixed,',') === FALSE)
    {
     if (strpos($mixed,'.') !== FALSE)
     {
      $r = '';
      $e = explode('.',$mixed);
      $r .= '`'.$e[0].'`';
      if (isset($e[1])) {$r .= '.`'.$e[1].'`';}
      if (isset($e[2])) {$r .= '.`'.$e[2].'`';}
      $mixed = $r;
     }
     else {$mixed = '`'.$mixed.'`';}
    }
    elseif (substr($mixed[0],0,1) != '`' and substr($mixed[0],-1) != '`')
    {
     $mixed = '`'.$mixed.'`';
    }
   }
   elseif ($mode == 3)
   {
   }
  }
  return $mixed;
 }
}
class SmartDB_mysql_select extends SmartDB_mysql_query
{
 public $from;
 public $join = array();
 public $fields = array();
 public $where = array();
 public $offset = NULL;
 public $limit = NULL;
 public $type = '';
 public $qtype = 'SELECT';
 public $order = array();
 public function __construct($fields,$driver)
 {
  $this->fields = $fields;
  $this->driver = $driver;
  $this->driver->lastQuery = $this;
 }
 public function from($mixed)
 {
  $this->from = $mixed;
  $this->_from = $this->prepare_mixed($this->from,2);
  return $this;
 }
 public function type($mixed)
 {
  $this->type = $mixed;
  return $this;
 }
 public function where($where)
 {
  if (is_scalar($where)) {$where = array($where);}
  if (is_array($where))
  {
   for ($i = 0,$k = array_keys($where),$s = sizeof($where); $i < $s; ++$i)
   {
    $this->where[] = array($k[$i],$where[$k[$i]]);
   }
  }
  return $this;
 }
 public function resetWhere() {$this->where = array();}
 public function limit($a,$b = NULL)
 {
  if ($b !== NULL)
  {
   $this->offset = (int) $a;
   $this->limit = (int) $b;
  }
  else {$this->limit = (int) $a;}
  return $this;
 }
 public function paginate($pagenum,$perpage,$max = NULL)
 {
  $this->offset = $pagenum*$perpage;
  $this->limit = (int) $perpage;
  if ($max !== NULL)
  {
   $s = $this->offset + $this->limit ;
   if ($s > $max)
   {
    if ($s - $max <= $this->limit) {$this->limit -= $s - $max;}
    else {$this->offset -= $s-$max;}
   }
  }
  return $this;
 }
 public function fields($arr = NULL)
 {
  if (is_array($arr)) {$this->fields = $arr;}
  else {$this->fields = func_get_args();}
  return $this;
 }
 public function join($table,$on = NULL,$type = 'LEFT')
 {
  $this->join[] = array($table,$on,$type);
  return $this;
 }
 public function order($expr,$desc = FALSE)
 {
  $this->order[] = array($expr,$desc);
  return $this;
 }
 public function resetOrder() {$this->order = array();}
 public function get_query()
 {
  if ($this->qcache !== NULL)
  {
   if (file_exists($p = $this->driver->qcache_path.$this->qcache))
   {
    return file_get_contents($p);
   }
  }
  $join = '';
  for ($i = 0,$s = sizeof($this->join); $i < $s; ++$i)
  {
   $_tbl = $this->prepare_mixed($this->join[$i][0],2);
   if ($this->join[$i][1] === NULL) {$on = $this->_from.'.`id` = '.$_tbl.'.`id`';}
   else {$on = $this->prepare_mixed($this->join[$i][1],3);}
   $join .= ' '.$this->join[$i][2]
						.' JOIN '.$_tbl
						.' ON '.$on;
  }
  $query = 'SELECT'.($this->type !== ''?' '.$this->type:'').' '.$this->prepare_mixed($this->fields,1)
					.($this->from !== NULL?' FROM '.$this->_from:'')
					.$join
					.(sizeof($this->where)?' WHERE '.$this->prepare_mixed($this->where,3):'')
					.(sizeof($this->order)?' ORDER BY '.$this->prepare_mixed($this->order,4):'')
					.(($this->limit !== NULL || $this->offset !== NULL)?' LIMIT '.($this->offset > 0?$this->offset.',':'').$this->limit:'')
	;
	if ($this->qcache !== NULL) {file_put_contents($p,$query);}
	return $query;
 }
}
class SmartDB_mysql_insert extends SmartDB_mysql_query
{
 public $type = '';
 public $table = '';
 public $postfix = '';
 public function __construct($table,$qtype,$driver,$row = NULL,$multi = NULL,$type = NULL,$unbuffered = TRUE)
 {
  $this->table = $table;
  $this->qtype = $qtype;
  $this->rows = array();
  $this->driver = $driver;
  $this->driver->lastQuery = $this;
  $this->unbuffered = (bool) $unbuffered;
  if ($type !== NULL) {$this->type = $type;}
  if ($row !== NULL)
  {
   if ($multi) {$this->rows = $row;}
   else {$this->rows[] = $row;}
   $this->execute();
  }
 }
 public function type($mixed)
 {
  $this->type = $mixed;
  return $this;
 }
 public function postfix($mixed)
 {
  $this->mixed = $mixed;
  return $this;
 }
 public function row($row)
 {
  $this->rows[] = $row;
  return $this;
 }
 public function rows($rows)
 {
  $this->rows += $rows;
  return $this;
 }
 public function get_query()
 {
  $s = sizeof($this->rows);
  if (!$s) {return NULL;}
  $arr_str_val = array();
  $str_fields = '';
  for ($j = 0; $j < $s; ++$j)
  {
   $this->_fields = $this->rows[$j];
   if (!is_array($this->_fields) or !sizeof($this->_fields)) {continue;}
   $str_val = '';
   if ($str_fields === '')
   {
    $ss = sizeof($this->_fields);
    $i = 0;
    foreach (array_keys($this->_fields) as $field) {$str_fields .= ($i > 0?', ':'').$this->prepare_mixed($field,2); ++$i;}
   }
   $i = 0;
   foreach($this->_fields as $val) {$str_val .= ($i > 0?', ':'').$this->driver->process_val($val); ++$i;}
   $arr_str_val[] = $str_val;
  }
  $query = $this->qtype.($this->type != ''?' '.$this->type:'').' INTO '.$this->prepare_mixed($this->table,2).' ( '.$str_fields.' ) VALUES ';
  foreach($arr_str_val as $k=>$v) {$query .= ($k > 0?', ':'').'('.$v.')';}
  $query .= ($this->postfix != ''?' '.$this->postfix:'');
  $this->rows = array();
  return $query;
 }
}
class SmartDB_mysql_update extends SmartDB_mysql_query
{
 public $type;
 public $table;
 public $where = array();
 public $postfix = '';
 public $limit = NULL;
 public $offset = 0;
 public function __construct($table,$driver,$row = NULL,$where = NULL,$limit = NULL,$type = NULL,$postfix = NULL,$unbuffered = TRUE)
 {
  $this->table = $table;
  $this->driver = $driver;
  $this->driver->lastQuery = $this;
  if ($row !== NULL) {$this->row = $row;}
  else {$this->row = array();}
  if ($type !== NULL) {$this->type = $type;}
  if ($where !== NULL) {$this->where = $where;}
  if ($limit !== NULL) {$this->limit = $limit;}
  if ($postfix !== NULL) {$this->postfix = $postfix;}
  $this->unbuffered = (bool) $unbuffered;
  if ($row !== NULL)
  {
   $this->execute();
  }
 }
 public function type($mixed)
 {
  $this->type = $mixed;
  return $this;
 }
 public function postfix($mixed)
 {
  $this->mixed = $mixed;
  return $this;
 }
 public function row($row)
 {
  $this->row = $row;
  return $this;
 }
 public function get_query()
 {
  if (!sizeof($this->row)) {return FALSE;}
  $query = 'UPDATE'.($this->type != ''?' '.$this->type:'')
	          .' '.$this->prepare_mixed($this->table,2)
	          .' SET ';
	$i = 0;
  foreach($this->row as $k=>$v) {$query .= ($i > 0?', ':'').'`'.$k.'` = '.$this->driver->process_val($v); ++$i;}
  $query .= (sizeof($this->where)?' WHERE '.$this->prepare_mixed($this->where,3):'')
					  .(($this->limit !== NULL || $this->offset > 0)?' LIMIT '.($this->offset > 0?$this->offset.',':'').$this->limit:'')
					  .($this->postfix != ''?' '.$this->postfix:'');
  return $query;
 }
}
class SmartDB_mysql_delete extends SmartDB_mysql_query
{
 public $type;
 public $fields;
 public $table;
 public $where = array();
 public $postfix = '';
 public $limit = NULL;
 public $offset = 0;
 public function __construct($table,$driver,$fields = NULL,$where = NULL,$limit = NULL,$type = NULL,$postfix = NULL,$unbuffered = TRUE)
 {
  $this->table = $table;
  $this->driver = $driver;
  $this->driver->lastQuery = $this;
  if ($fields !== NULL) {$this->fields = $fields;}
  if ($type !== NULL) {$this->type = $type;}
  if ($where !== NULL) {$this->where = $where;}
  if ($limit !== NULL) {$this->limit = $limit;}
  if ($postfix !== NULL) {$this->postfix = $postfix;}
  $this->unbuffered = (bool) $unbuffered;
  if ($row !== NULL)
  {
   $this->execute();
  }
 }
 public function type($mixed)
 {
  $this->type = $mixed;
  return $this;
 }
 public function postfix($mixed)
 {
  $this->mixed = $mixed;
  return $this;
 }
 public function fields($mixed)
 {
  $this->fields = $mixed;
  return $this;
 }
 public function get_query()
 {
  if (!sizeof($this->row)) {return FALSE;}
  $query = 'DELETE'.($this->type != ''?' '.$this->type:'').($this->fields != ''?' '.$this->prepare_mixed($this->fields,1):'')
	          .' FROM '.$this->prepare_mixed($this->table,2)
	          .(sizeof($this->where)?' WHERE '.$this->prepare_mixed($this->where,3):'')
					  .(($this->limit !== NULL || $this->offset > 0)?' LIMIT '.($this->offset > 0?$this->offset.',':'').$this->limit:'')
					  .($this->postfix != ''?' '.$this->postfix:'');
  return $query;
 }
}
define('SMARTDB_MYSQL_LOADED',TRUE);
return self::$db = new SmartDB_mysql;
}